use std::sync::Arc;

use super::user_service::UserService;
use crate::errors::cons_error;
use crate::mdb::{MClient, MDB};
use crate::models::account::GiteePlatformUserInfo;
use crate::{AppError, AppResult, Config};
use awc::Client;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use wither::bson::DateTime;

#[derive(Debug, Clone)]
pub struct GiteeService {
    pub config: Arc<Config>,
    pub user_service: Arc<UserService>,
    pub mdb: MDB,
    pub m_client: MClient,
}

impl GiteeService {
    pub fn new(
        config: Arc<Config>,
        user_service: Arc<UserService>,
        mdb: MDB,
        m_client: MClient,
    ) -> Self {
        Self {
            config,
            user_service,
            mdb,
            m_client,
        }
    }
    pub fn get_login_url<T: Into<String>>(&self, redirect_url: T) -> AppResult<String> {
        if let Some(client_id) = &self.config.gitee.client_id {
            Ok(format!(
                "{}/oauth/authorize?client_id={}&redirect_uri={}&scope={}&response_type=code",
                gitee_urls::BASE,
                client_id,
                redirect_url.into(),
                self.config.gitee.scope.clone().join(" ")
            ))
        } else {
            Err(AppError::ConsError(cons_error::MSG_GITHUB_NOT_CONFIG))
        }
    }
    pub async fn get_access_token_from_code(
        &self,
        code: &str,
        redirect_uri: &str,
    ) -> AppResult<String> {
        if let (Some(client_id), Some(client_secret)) = (
            &self.config.gitee.client_id,
            &self.config.gitee.client_secret,
        ) {
            let client = Client::default();
            #[derive(Debug, Serialize)]
            struct SendForm {
                grant_type: &'static str,
                client_id: String,
                client_secret: String,
                code: String,
                redirect_uri: String,
            }
            let res: Value = client
                .post(format!("{}/oauth/token", gitee_urls::BASE,))
                .insert_header((actix_http::header::ACCEPT, "application/json"))
                .send_form(&SendForm {
                    grant_type: "authorization_code",
                    client_id: client_id.clone(),
                    client_secret: client_secret.clone(),
                    code: code.to_string(),
                    redirect_uri: redirect_uri.to_string(),
                })
                .await
                .map_err(actix_web::error::ErrorInternalServerError)?
                .json()
                .await
                .map_err(actix_web::error::ErrorInternalServerError)?;
            if let Value::String(res) = &res["access_token"] {
                log::info!("gitee 成功获取 access_token: {}", res);
                return Ok(res.clone());
            }
            log::debug!("gitee 获取access_token 失败 {:?}", res);
            Err(AppError::other_error(res))
        } else {
            Err(AppError::ConsError(cons_error::MSG_GITHUB_NOT_CONFIG))
        }
    }
    pub async fn get_user_info_from_access_token(
        &self,
        access_token: String,
    ) -> AppResult<GiteePlatformUserInfo> {
        let client = Client::default();
        let res: GiteeLoginResponse = client
            .get(format!(
                "{}/user?access_token={}",
                gitee_urls::API,
                access_token
            ))
            .insert_header((actix_http::header::ACCEPT, "application/json"))
            .insert_header((actix_http::header::USER_AGENT, "awc/3.0.0"))
            .send()
            .await
            .map_err(actix_web::error::ErrorInternalServerError)?
            .json()
            .await
            .map_err(actix_web::error::ErrorInternalServerError)?;
        Ok(GiteePlatformUserInfo::from(res))
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GiteeLoginResponse {
    pub avatar_url: String,
    pub bio: String,
    pub blog: Option<String>,
    pub email: Option<String>,
    pub events_url: String,
    pub followers: i64,
    pub followers_url: String,
    pub following: i64,
    pub following_url: String,
    pub gists_url: String,
    pub html_url: String,
    pub id: i64,
    pub login: String,
    pub member_role: Option<String>,
    pub name: String,
    pub organizations_url: String,
    pub public_gists: i64,
    pub public_repos: i64,
    pub received_events_url: String,
    pub remark: String,
    pub repos_url: String,
    pub stared: i64,
    pub starred_url: String,
    pub subscriptions_url: String,
    pub r#type: String,
    pub url: String,
    pub watched: i64,
    pub weibo: Option<String>,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
}
impl From<GiteeLoginResponse> for GiteePlatformUserInfo {
    fn from(r: GiteeLoginResponse) -> Self {
        Self {
            avatar_url: r.avatar_url,
            bio: r.bio,
            blog: r.blog,
            email: r.email,
            events_url: r.events_url,
            followers: r.followers,
            followers_url: r.followers_url,
            following: r.following,
            following_url: r.following_url,
            gists_url: r.gists_url,
            html_url: r.html_url,
            id: r.id,
            login: r.login,
            member_role: r.member_role,
            name: r.name,
            organizations_url: r.organizations_url,
            public_gists: r.public_gists,
            public_repos: r.public_repos,
            received_events_url: r.received_events_url,
            remark: r.remark,
            repos_url: r.repos_url,
            stared: r.stared,
            starred_url: r.starred_url,
            subscriptions_url: r.subscriptions_url,
            r#type: r.r#type,
            url: r.url,
            watched: r.watched,
            weibo: r.weibo,
            created_at: DateTime::from_chrono(r.created_at),
            updated_at: DateTime::from_chrono(r.updated_at),
        }
    }
}
mod gitee_urls {
    pub static BASE: &str = "https://gitee.com";
    pub static API: &str = "https://gitee.com/api/v5";
}
